package com.dave.gd.core.common.globalsession.filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.dave.gd.core.common.globalsession.GlobalHttpSession;
import com.dave.gd.core.common.globalsession.GlobalSessionFilterSettings;
import com.dave.gd.core.common.globalsession.GlobalSessionHttpRequest;
import com.dave.gd.core.common.globalsession.StoreKeyGenerator;
import com.dave.gd.core.common.globalsession.manager.SessionManager;

public abstract class GlobalSessionFilter implements Filter {
    
    private static Logger log = LoggerFactory.getLogger(GlobalSessionFilter.class);
    private static final String METADATA_KEY = "__metadata__";
    private static final String GLOBAL_NAMESPACE = "GLOBAL";
    private static final String DEFAULT_SESSION_ID_NAME = "__gsid__";
    
    public static class ConfigKey {
        public static final String NAMESPACE = "namespace";
        public static final String SESSION_ID = "sessionId";
        public static final String DOMAIN = "domain";
        public static final String PATH = "path";
        public static final String SECURE = "secure";
        public static final String HTTP_ONLY = "httpOnly";
        public static final String SESSION_TIMEOUT = "sessionTimeout";
        public static final String EXCLUDE_REG_EXP = "excludeRegExp";
        public static final String LOGIN_PAGE = "loginPage";
        public static final String LOGIN_SERVLET = "loginServlet";
    }
    
    public static class RequestAttributeKey {
        protected static final String SESSION_STATUS = "__sessionStatus__";
    }
    
    static enum SessionStatus {
        unknown, fixed
    }
    
    protected GlobalSessionFilterSettings settings;
    
    /**
     * 将web.xml中关于session的配置参数转成GlobalSessionFilterSettings对象
     */
    protected GlobalSessionFilterSettings getGlobalSessionFilterSettings(FilterConfig config) {
        
        GlobalSessionFilterSettings settings = new GlobalSessionFilterSettings();
        
        settings.setNamespace(getConfigValue(config, ConfigKey.NAMESPACE));
        if (settings.getNamespace() == null) {
            settings.setNamespace(GLOBAL_NAMESPACE);
        }
        
        settings.setExcludeRegExp(getConfigValue(config, ConfigKey.EXCLUDE_REG_EXP));
        
        settings.setSessionIdKey(getConfigValue(config, ConfigKey.SESSION_ID));
        if (settings.getSessionIdKey() == null) {
            settings.setSessionIdKey(DEFAULT_SESSION_ID_NAME);
        }
        
        settings.setDomain(getConfigValue(config, ConfigKey.DOMAIN));
        
        settings.setPath(getConfigValue(config, ConfigKey.PATH));
        if (settings.getPath() == null) {
            settings.setPath("/");
        }
        
        settings.setSecure(getConfigValue(config, ConfigKey.SECURE) != null
            && getConfigValue(config, ConfigKey.SECURE).equals("true"));
        
        settings.setHttpOnly(getConfigValue(config, ConfigKey.HTTP_ONLY) != null
            && getConfigValue(config, ConfigKey.HTTP_ONLY).equals("true"));
        
        String sessionTimeout = getConfigValue(config, ConfigKey.SESSION_TIMEOUT);
        if (sessionTimeout == null) {
            settings.setSessionTimeoutMinutes(10);
        } else {
            settings.setSessionTimeoutMinutes(Integer.valueOf(sessionTimeout));
        }
        
        settings.setLoginPage(getConfigValue(config, ConfigKey.LOGIN_PAGE));
        settings.setLoginServlet(getConfigValue(config, ConfigKey.LOGIN_SERVLET));
        
        return settings;
    }
    
    /**
     * 查找name为__GSID__所对应的合法Cookie
     */
    protected Cookie getCurrentValidSessionIdCookie(HttpServletRequest req) {
        if (req.getCookies() != null) {
            for (Cookie cookie : req.getCookies()) {
                if (cookie.getName().equals(settings.getSessionIdKey())
                    && cookie.getValue() != null && cookie.getValue().trim().length() > 0) {
                    if (isValidSession(createGlobalSessionRequest(req, cookie.getValue()))) {
                        if (log.isDebugEnabled()) {
                            log.debug("SessionId cookie is found. (" + settings.getSessionIdKey()
                                + " -> " + cookie.getValue() + ")");
                        }
                        return cookie;
                    } else {
                        if (log.isDebugEnabled()) {
                            log.debug("SessionId cookie is found but it's invalid. ("
                                + settings.getSessionIdKey() + " -> " + cookie.getValue() + ")");
                        }
                        continue;
                    }
                }
            }
        }
        if (log.isDebugEnabled()) {
            log.debug("SessionId cookie is not found.");
        }
        return null;
    }
    
    /**
     * 查找name为__GSID__所对应的sessionId
     */
    protected String getCurrentCookieSessionId(HttpServletRequest req) {
        if (req.getCookies() != null) {
            for (Cookie cookie : req.getCookies()) {
                if (cookie.getName().equals(settings.getSessionIdKey())
                    && cookie.getValue() != null && cookie.getValue().trim().length() > 0) {
                    if (log.isDebugEnabled()) {
                        log.debug("Cookie sessionId  is found. (" + settings.getSessionIdKey()
                            + " -> " + cookie.getValue() + ")");
                    }
                    return cookie.getValue();
                    
                }
            }
        }
        if (log.isDebugEnabled()) {
            log.debug("Cookie sessionId  is not found.");
        }
        return null;
    }
    
    protected GlobalSessionHttpRequest createGlobalSessionRequest(HttpServletRequest req,
        String sessionIdValue) {
        return new GlobalSessionHttpRequest(req, sessionIdValue, settings.getNamespace(),
            settings.getSessionTimeoutMinutes());
    }
    
    @Override
    public void init(FilterConfig config) throws ServletException {
        settings = getGlobalSessionFilterSettings(config);
    }
    
    /**
     * doFilter方法用来执行真正的过滤操作
     */
    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
        throws IOException, ServletException {
        HttpServletRequest _req = (HttpServletRequest) req;
        HttpServletResponse _res = (HttpServletResponse) res;
        if (isGlobalSessionHttpRequest(_req)) {
            
            if (log.isDebugEnabled()) {
                log.debug("GlobalSessionHttpRequest is already applied.");
            }
            chain.doFilter(_req, _res);
        } else if (_req.getRequestURI().endsWith(settings.getLoginPage())
            || _req.getRequestURI().endsWith(settings.getLoginServlet())) {
            chain.doFilter(_req, _res);
        } else if (settings.getExcludeRegExp() != null
            && _req.getRequestURI().matches(settings.getExcludeRegExp())) {
            
            if (log.isDebugEnabled()) {
                log.debug("This URI is excluded. (URI: " + _req.getRequestURI() + ")");
            }
            chain.doFilter(_req, _res);
            
        } else {
            
            // 判断用户是否登录
            String currentSessionId = getCurrentCookieSessionId(_req);
            SessionManager sm = new SessionManager();
            StoreKeyGenerator keyGenerator = new StoreKeyGenerator(currentSessionId,
                GLOBAL_NAMESPACE);
            ;
            if (currentSessionId == null
                || !sm.isSessionExists(keyGenerator.generate(METADATA_KEY))) {
                _res.sendRedirect(settings.getLoginPage());
                return;
            }
            
            // doFilter with the request wrapper
            GlobalSessionHttpRequest _wrappedReq = createGlobalSessionRequest(_req,
                currentSessionId);
            chain.doFilter(_wrappedReq, _res);
            
            // update attributes, expiration
            GlobalHttpSession session = _wrappedReq.getSession();
            session.reloadAttributes(); // need reloading from the store to work
                                        // with GlassFish
            session.save();
            
        }
    }
    
    @Override
    public void destroy() {
    }
    
    protected static String getConfigValue(FilterConfig config, String keyName) {
        String fromInitParam = config.getInitParameter(keyName);
        if (fromInitParam != null) {
            return fromInitParam;
        }
        return System.getProperty(keyName);
    }
    
    protected static void setSessionStatus(HttpServletRequest req, SessionStatus status) {
        req.setAttribute(RequestAttributeKey.SESSION_STATUS, status);
        
    }
    
    protected static SessionStatus getSessionStatus(HttpServletRequest req) {
        Object status = req.getAttribute(RequestAttributeKey.SESSION_STATUS);
        if (status == null) {
            return SessionStatus.unknown;
        } else {
            return (SessionStatus) status;
        }
    }
    
    protected static boolean isValidSession(GlobalSessionHttpRequest req) {
        if (getSessionStatus(req) == SessionStatus.fixed) {
            return true;
        }
        return req.getSession().isValid();
    }
    
    protected static boolean isGlobalSessionHttpRequest(HttpServletRequest req) {
        return req.getSession() instanceof GlobalHttpSession;
    }
    
}
